﻿using System;
using System.Collections.Generic;
using System.Text;

namespace SimpleEntry
{
	/*
		Canonical example to explore ways to implement non-trivial
		formatting
	*/
	public class IntegerConverter : IConverter
	{
		////////////////////////////////////////////////////////
		// Interface. Delegate to internal overridable mothoes
		public String Format(object o) { return internalFormat(o); }

		public object Parse(String value, out String parseErrorMessage) { return internalParse(value,  out parseErrorMessage); }

		public bool Validate(object o, out String errorMessage) { return internalValidate(o, out errorMessage); }

		////////////////////////////////////////////////////////
	
		//////////// Constructors//////////////////////////////////
		//
		// simplest version. Same as VanillaIntegerConverter
		public IntegerConverter()
		{
			NullText = ""; // default show empty for null 
		}

		// 
		///////////////////////////////////////////////////////////

		// Text representing a null value. Scenario is a display either of a record number
		// or if null text such as "New Record".
		public String NullText {get; set; }


		protected virtual string internalFormat(object o)
		{
			// empty case.
			if (IsObjectNull(o)) return NullText;
			
			return o.ToString();
		}

		protected virtual object internalParse(String value, out String parseErrorMessage)
		{
			parseErrorMessage = "";
			
			// Base processing now is allow null and represent this as System.DbNull
			if (value.Trim().Length == 0 || value.Trim().ToUpper() == NullText.ToUpper())
				return System.DBNull.Value;
			
			// allow for ,
			value = value.Replace(",", "");

			int iVal;
			if (int.TryParse(value, out iVal))
				return iVal;
			// error
			parseErrorMessage = GetParseErrorMessage();
			return null;
		}

		protected virtual String GetParseErrorMessage() { return INT_PARSE_ERROR; }

		protected virtual bool internalValidate(object o, out string errorMessage)
		{
			errorMessage = "";

			if (o.GetType() != typeof(int))
			{
				errorMessage = String.Format("Internal validation error: type {0} is wrong for an integer", o.GetType());
				return false;
			}

			errorMessage = "";
			return true;
		}

		protected bool IsObjectNull(object o)
		{
			if (o == null) return true;
			if (o.GetType() == typeof(System.DBNull)) return true;
			return false;
		}

		// override with GetParseErrorMessage
		const String INT_PARSE_ERROR = @"
Incorrect format for a whole number. 
Expected input is digits from 0-9. 
";
	}

	/*
		Demonstration override of the integer converter to put commas in the 
		display via an override to the internalFormat method.
		Note: there is no need to override the parse method as the base
		class removes commas anyway.
	  
	 */
	public class CommaDisplayIntegerConverter : IntegerConverter
	{
		public CommaDisplayIntegerConverter() : base() { }

		protected override string internalFormat(object o)
		{
			// empty case.
			if (IsObjectNull(o)) return NullText;

			// add comma option
			String s = o.ToString();
			int negAdjust = (((int)o) < 0) ? 1 : 0;
			int l = s.Length;
			if (l > 3 + negAdjust)
			{
				List<int> iPos = new List<int>();

				while (l > 3 + negAdjust)
				{
					iPos.Add(l - 3);
					l = l - 3;
				}
				foreach (int i in iPos)
				{
					s = s.Insert(i, ",");
				}
			}
			return s;
		}
		
		protected override string GetParseErrorMessage()
		{
			return base.GetParseErrorMessage() + String.Format("\n(Error courtesy of class: {0})", this.GetType());
		}

	}
}
